// Copyright 2016 The PDFium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com

#ifndef CORE_FPDFAPI_PARSER_CPDF_DOCUMENT_H_
#define CORE_FPDFAPI_PARSER_CPDF_DOCUMENT_H_

#include <memory>
#include <set>
#include <utility>
#include <vector>

#include "core/fpdfapi/parser/cpdf_dictionary.h"
#include "core/fpdfapi/parser/cpdf_parser.h"
#include "core/fxcrt/fx_memory.h"
#include "core/fxcrt/observed_ptr.h"
#include "core/fxcrt/retain_ptr.h"
#include "core/fxcrt/span.h"
#include "core/fxcrt/unowned_ptr.h"

class CPDF_ReadValidator;
class CPDF_StreamAcc;
class IFX_SeekableReadStream;
class JBig2_DocumentContext;

class CPDF_Document : public Observable,
                      public CPDF_Parser::ParsedObjectsHolder {
 public:
  // Type from which the XFA extension can subclass itself.
  class Extension {
   public:
    virtual ~Extension() = default;
    virtual int GetPageCount() const = 0;
    virtual uint32_t DeletePage(int page_index) = 0;
    virtual bool ContainsExtensionForm() const = 0;
    virtual bool ContainsExtensionFullForm() const = 0;
    virtual bool ContainsExtensionForegroundForm() const = 0;
  };

  class LinkListIface {
   public:
    // CPDF_Document merely helps manage the lifetime.
    virtual ~LinkListIface() = default;
  };

  class PageDataIface {
   public:
    PageDataIface();
    virtual ~PageDataIface();

    virtual void ClearStockFont() = 0;
    virtual RetainPtr<CPDF_StreamAcc> GetFontFileStreamAcc(
        RetainPtr<const CPDF_Stream> pFontStream) = 0;
    virtual void MaybePurgeFontFileStreamAcc(
        RetainPtr<CPDF_StreamAcc>&& pStreamAcc) = 0;
    virtual void MaybePurgeImage(uint32_t objnum) = 0;

    void SetDocument(CPDF_Document* pDoc) { m_pDoc = pDoc; }

   protected:
    CPDF_Document* GetDocument() const { return m_pDoc; }

   private:
    UnownedPtr<CPDF_Document> m_pDoc;
  };

  class RenderDataIface {
   public:
    RenderDataIface();
    virtual ~RenderDataIface();

    void SetDocument(CPDF_Document* pDoc) { m_pDoc = pDoc; }

   protected:
    CPDF_Document* GetDocument() const { return m_pDoc; }

   private:
    UnownedPtr<CPDF_Document> m_pDoc;
  };

  static constexpr int kPageMaxNum = 0xFFFFF;

  static bool IsValidPageObject(const CPDF_Object* obj);

  CPDF_Document(std::unique_ptr<RenderDataIface> pRenderData,
                std::unique_ptr<PageDataIface> pPageData);
  ~CPDF_Document() override;

  Extension* GetExtension() const { return m_pExtension.get(); }
  void SetExtension(std::unique_ptr<Extension> pExt) {
    m_pExtension = std::move(pExt);
  }

  CPDF_Parser* GetParser() const { return m_pParser.get(); }
  const CPDF_Dictionary* GetRoot() const { return m_pRootDict.Get(); }
  RetainPtr<CPDF_Dictionary> GetMutableRoot() { return m_pRootDict; }
  RetainPtr<CPDF_Dictionary> GetInfo();
  RetainPtr<const CPDF_Array> GetFileIdentifier() const;

  // Returns the object number for the deleted page, or 0 on failure.
  uint32_t DeletePage(int iPage);
  // `page_obj_num` is the return value from DeletePage(). If it is non-zero,
  // and it is no longer used in the page tree, then replace the page object
  // with a null object.
  void SetPageToNullObject(uint32_t page_obj_num);
  bool MovePages(pdfium::span<const int> page_indices, int dest_page_index);

  int GetPageCount() const;
  bool IsPageLoaded(int iPage) const;
  RetainPtr<const CPDF_Dictionary> GetPageDictionary(int iPage);
  RetainPtr<CPDF_Dictionary> GetMutablePageDictionary(int iPage);
  int GetPageIndex(uint32_t objnum);
  // When `get_owner_perms` is true, returns full permissions if unlocked by
  // owner.
  uint32_t GetUserPermissions(bool get_owner_perms) const;

  // PageDataIface wrappers, try to avoid explicit getter calls.
  RetainPtr<CPDF_StreamAcc> GetFontFileStreamAcc(
      RetainPtr<const CPDF_Stream> pFontStream);
  void MaybePurgeFontFileStreamAcc(RetainPtr<CPDF_StreamAcc>&& pStreamAcc);
  void MaybePurgeImage(uint32_t objnum);

  // Returns a valid pointer, unless it is called during destruction.
  PageDataIface* GetPageData() const { return m_pDocPage.get(); }
  RenderDataIface* GetRenderData() const { return m_pDocRender.get(); }

  void SetPageObjNum(int iPage, uint32_t objNum);

  JBig2_DocumentContext* GetOrCreateCodecContext();
  LinkListIface* GetLinksContext() const { return m_pLinksContext.get(); }
  void SetLinksContext(std::unique_ptr<LinkListIface> pContext) {
    m_pLinksContext = std::move(pContext);
  }

  // Behaves like NewIndirect<CPDF_Stream>(dict), but keeps track of the object
  // number assigned to the newly created stream.
  RetainPtr<CPDF_Stream> CreateModifiedAPStream(
      RetainPtr<CPDF_Dictionary> dict);

  // Returns whether CreateModifiedAPStream() created `stream`.
  bool IsModifiedAPStream(const CPDF_Stream* stream) const;

  // CPDF_Parser::ParsedObjectsHolder:
  bool TryInit() override;
  RetainPtr<CPDF_Object> ParseIndirectObject(uint32_t objnum) override;

  CPDF_Parser::Error LoadDoc(RetainPtr<IFX_SeekableReadStream> pFileAccess,
                             const ByteString& password);
  CPDF_Parser::Error LoadLinearizedDoc(RetainPtr<CPDF_ReadValidator> validator,
                                       const ByteString& password);
  bool has_valid_cross_reference_table() const {
    return m_bHasValidCrossReferenceTable;
  }

  void LoadPages();
  void CreateNewDoc();
  RetainPtr<CPDF_Dictionary> CreateNewPage(int iPage);

  void IncrementParsedPageCount() { ++m_ParsedPageCount; }
  uint32_t GetParsedPageCountForTesting() { return m_ParsedPageCount; }

  void SetRootForTesting(RetainPtr<CPDF_Dictionary> root);

 protected:
  void SetParser(std::unique_ptr<CPDF_Parser> pParser);

  void ResizePageListForTesting(size_t size);

 private:
  class StockFontClearer {
   public:
    FX_STACK_ALLOCATED();

    explicit StockFontClearer(CPDF_Document::PageDataIface* pPageData);
    ~StockFontClearer();

   private:
    UnownedPtr<CPDF_Document::PageDataIface> const m_pPageData;
  };

  // Retrieve page count information by getting count value from the tree nodes
  int RetrievePageCount();

  // When this method is called, m_pTreeTraversal[level] exists.
  RetainPtr<CPDF_Dictionary> TraversePDFPages(int iPage,
                                              int* nPagesToGo,
                                              size_t level);

  RetainPtr<const CPDF_Dictionary> GetPagesDict() const;
  RetainPtr<CPDF_Dictionary> GetMutablePagesDict();

  bool InsertDeletePDFPage(RetainPtr<CPDF_Dictionary> pages_dict,
                           int pages_to_go,
                           RetainPtr<CPDF_Dictionary> page_dict,
                           bool is_insert,
                           std::set<RetainPtr<CPDF_Dictionary>>* visited);

  bool InsertNewPage(int iPage, RetainPtr<CPDF_Dictionary> pPageDict);
  void ResetTraversal();
  CPDF_Parser::Error HandleLoadResult(CPDF_Parser::Error error);

  std::unique_ptr<CPDF_Parser> m_pParser;
  RetainPtr<CPDF_Dictionary> m_pRootDict;
  RetainPtr<CPDF_Dictionary> m_pInfoDict;

  // Vector of pairs to know current position in the page tree. The index in the
  // vector corresponds to the level being described. The pair contains a
  // pointer to the dictionary being processed at the level, and an index of the
  // of the child being processed within the dictionary's /Kids array.
  std::vector<std::pair<RetainPtr<CPDF_Dictionary>, size_t>> m_pTreeTraversal;

  // True if the CPDF_Parser succeeded without having to rebuild the cross
  // reference table.
  bool m_bHasValidCrossReferenceTable = false;

  // Index of the next page that will be traversed from the page tree.
  bool m_bReachedMaxPageLevel = false;
  int m_iNextPageToTraverse = 0;
  uint32_t m_ParsedPageCount = 0;

  std::unique_ptr<RenderDataIface> const m_pDocRender;
  // Must be after `m_pDocRender`.
  std::unique_ptr<PageDataIface> const m_pDocPage;
  std::unique_ptr<JBig2_DocumentContext> m_pCodecContext;
  std::unique_ptr<LinkListIface> m_pLinksContext;
  std::set<uint32_t> m_ModifiedAPStreamIDs;
  std::vector<uint32_t> m_PageList;  // Page number to page's dict objnum.

  // Must be second to last.
  StockFontClearer m_StockFontClearer;

  // Must be last. Destroy the extension before any non-extension teardown.
  std::unique_ptr<Extension> m_pExtension;
};

#endif  // CORE_FPDFAPI_PARSER_CPDF_DOCUMENT_H_

 
`CPDF_Document` 类提供了一组接口功能用于处理 PDF 文档对象。以下是该接口的功能列表：

### 内部类和接口
1. **Extension**: 可扩展的子类接口，包括基本 PDF 文档操作方法，如获取页数和删除页面等。
2. **LinkListIface**: 链接列表接口，用于管理链接列表生命周期。
3. **PageDataIface**: 页面数据接口，管理文档中的页面数据及相关资源。
4. **RenderDataIface**: 渲染数据接口，管理与文档渲染相关的数据。

### 静态方法
5. **static bool IsValidPageObject(const CPDF_Object* obj)**: 检查对象是否是有效的页面对象。

### 构造与析构函数
6. **CPDF_Document(std::unique_ptr<RenderDataIface> pRenderData, std::unique_ptr<PageDataIface> pPageData)**: 构造函数。
7. **~CPDF_Document() override**: 析构函数。

### 拓展功能
8. **Extension* GetExtension() const**: 获取扩展接口。
9. **void SetExtension(std::unique_ptr<Extension> pExt)**: 设置扩展接口。

### 解析器及根字典操作
10. **CPDF_Parser* GetParser() const**: 获取解析器。
11. **const CPDF_Dictionary* GetRoot() const**: 获取根字典。
12. **RetainPtr<CPDF_Dictionary> GetMutableRoot()**: 获取可修改的根字典。
13. **RetainPtr<CPDF_Dictionary> GetInfo()**: 获取信息字典。
14. **RetainPtr<const CPDF_Array> GetFileIdentifier() const**: 获取文件标识符。

### 页面操作
15. **uint32_t DeletePage(int iPage)**: 删除指定页面，返回页面对象号码，失败返回0。
16. **void SetPageToNullObject(uint32_t page_obj_num)**: 将指定页面对象设置为空对象。
17. **bool MovePages(pdfium::span<const int> page_indices, int dest_page_index)**: 移动指定页面到指定位置。

### 页面检索和信息
18. **int GetPageCount() const**: 获取页数。
19. **bool IsPageLoaded(int iPage) const**: 检查页面是否已加载。
20. **RetainPtr<const CPDF_Dictionary> GetPageDictionary(int iPage)**: 获取页面字典。
21. **RetainPtr<CPDF_Dictionary> GetMutablePageDictionary(int iPage)**: 获取可修改的页面字典。
22. **int GetPageIndex(uint32_t objnum)**: 获取对象号码对应页面索引。
23. **uint32_t GetUserPermissions(bool get_owner_perms) const**: 获取用户权限。

### 页面数据和渲染数据操作
24. **RetainPtr<CPDF_StreamAcc> GetFontFileStreamAcc(RetainPtr<const CPDF_Stream> pFontStream)**: 获取字体文件流。
25. **void MaybePurgeFontFileStreamAcc(RetainPtr<CPDF_StreamAcc>&& pStreamAcc)**: 可能清除字体文件流。
26. **void MaybePurgeImage(uint32_t objnum)**: 可能清除图像。

27. **PageDataIface* GetPageData() const**: 获取页面数据。
28. **RenderDataIface* GetRenderData() const**: 获取渲染数据。

### 页面操作
29. **void SetPageObjNum(int iPage, uint32_t objNum)**: 设置页面对象号。

### 编解码与链接上下文
30. **JBig2_DocumentContext* GetOrCreateCodecContext()**: 获取或创建编解码上下文。
31. **LinkListIface* GetLinksContext() const**: 获取链接上下文。
32. **void SetLinksContext(std::unique_ptr<LinkListIface> pContext)**: 设置链接上下文。

### AP流操作
33. **RetainPtr<CPDF_Stream> CreateModifiedAPStream(RetainPtr<CPDF_Dictionary> dict)**: 创建修改后的 AP 流。
34. **bool IsModifiedAPStream(const CPDF_Stream* stream) const**: 检查是否创建了修改后的 AP 流。

### CPDF_Parser::ParsedObjectsHolder覆盖
35. **bool TryInit() override**: 尝试初始化。
36. **RetainPtr<CPDF_Object> ParseIndirectObject(uint32_t objnum) override**: 解析间接对象。

### 文档加载和页面树操作
37. **CPDF_Parser::Error LoadDoc(RetainPtr<IFX_SeekableReadStream> pFileAccess, const ByteString& password)**: 加载文档。
38. **CPDF_Parser::Error LoadLinearizedDoc(RetainPtr<CPDF_ReadValidator> validator, const ByteString& password)**: 加载线性化文档。
39. **bool has_valid_cross_reference_table() const**: 检查是否有有效的交叉引用表。
40. **void LoadPages()**: 加载页面。
41. **void CreateNewDoc()**: 创建新文档。
42. **RetainPtr<CPDF_Dictionary> CreateNewPage(int iPage)**: 创建新页面。

### 测试相关
43. **void IncrementParsedPageCount()**: 增加已解析页面数，用于测试。
44. **uint32_t GetParsedPageCountForTesting()**: 获取已解析页面数。

45. **void SetRootForTesting(RetainPtr<CPDF_Dictionary> root)**: 设置根字典，用于测试。
46. **void ResizePageListForTesting(size_t size)**: 调整页面列表大小。

这些功能构成了 `CPDF_Document` 类的接口，提供了 PDF 文档的基本操作和高级功能，包括页面操作、文档加载、解析和拓展性支持。

*/